Source code for hysop.operator.misc

# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from hysop.tools.decorators import debug
from hysop.tools.htypes import check_instance, first_not_None, to_tuple
from hysop.tools.transposition_states import TranspositionState
from hysop.constants import Backend, MemoryOrdering
from hysop.core.graph.computational_operator import ComputationalGraphOperator
from hysop.fields.continuous_field import Field
from hysop.core.graph.graph import op_apply


[docs] class Noop(ComputationalGraphOperator): """An operator that does nothing and implements apply as noop.""" @op_apply def apply(self, **kwds): """This is a noop.""" pass
[docs] @classmethod def supported_backends(cls): return Backend.all
[docs] @classmethod def supports_multiple_field_topologies(cls): return True
[docs] @classmethod def supports_multiple_topologies(cls): return True
[docs] @classmethod def supports_mpi(cls): return True
[docs] def get_node_requirements(self): from hysop.core.graph.node_requirements import OperatorRequirements reqs = OperatorRequirements( self, enforce_unique_transposition_state=False, enforce_unique_topology_shape=False, enforce_unique_memory_order=False, ) return reqs
[docs] class ForceTopologyState(Noop): """ Dummy operator used mostly for testing purposes where we need a consistant output topology and data layout (local transposition state, array backend, memory ordering). This operator will just impose a given transposition states, a given memory ordering and a given backend, to all of its input fields. This forces the graph generator to generate additional operators and topologies to comply with those field requirements. If transposition_state is not given, all input fields will be imposed to be in natural transposition order (YX in 2D and ZYX in 3D). If memory_order is not given, all input fields will be imposed to be C_CONTIGUOUS. If backend is not given, all input fields will be imposed to live on Backend.HOST. """ @debug def __new__( cls, fields, variables, tstate=None, memorder=None, backend=None, extra_kwds=None, mpi_params=None, cl_env=None, **kwds, ): kwds.setdefault("mpi_params", None) return super().__new__(cls, input_fields=None, output_fields=None, **kwds) @debug def __init__( self, fields, variables, tstate=None, memorder=None, backend=None, extra_kwds=None, mpi_params=None, cl_env=None, **kwds, ): extra_kwds = first_not_None(extra_kwds, {}) fields = to_tuple(fields) check_instance(fields, tuple, values=Field, minsize=1) f0 = fields[0] for f in fields[1:]: if f.domain.dim != f0.domain.dim: raise ValueError("Domain dimension mismatch.") dim = f0.domain.dim tstate = first_not_None(tstate, TranspositionState[dim].default()) memorder = first_not_None(memorder, MemoryOrdering.ANY) if tstate.dimension() != dim: msg = "Transposition state {} has dimension {} which is " msg += "incompatible with domain dimension {}." msg = msg.format(tstate, tstate.dimension(), dim) raise ValueError(msg) input_fields = {k: variables[k] for k in fields} output_fields = {k: variables[k] for k in fields} cl_env = first_not_None(cl_env, extra_kwds.get("cl_env", None)) mpi_params = first_not_None( mpi_params, extra_kwds.get("mpi_params", None), getattr(cl_env, "mpi_params", None), ) extra_kwds.setdefault("mpi_params", mpi_params) extra_kwds.setdefault("cl_env", cl_env) kwds.setdefault("mpi_params", mpi_params) super().__init__(input_fields=input_fields, output_fields=output_fields, **kwds) self.tstate = tstate self.memorder = memorder self.backend = first_not_None(backend, Backend.HOST) self.extra_kwds = first_not_None(extra_kwds, {})
[docs] @debug def get_field_requirements(self): from hysop.topology.topology_descriptor import TopologyDescriptor from hysop.fields.field_requirements import DiscreteFieldRequirements for field, topo_descriptor in self.input_fields.items(): topo_descriptor = TopologyDescriptor.build_descriptor( backend=self.backend, operator=self, field=field, handle=topo_descriptor, **self.extra_kwds, ) self.input_fields[field] = topo_descriptor for field, topo_descriptor in self.output_fields.items(): topo_descriptor = TopologyDescriptor.build_descriptor( backend=self.backend, operator=self, field=field, handle=topo_descriptor, **self.extra_kwds, ) self.output_fields[field] = topo_descriptor # and we use default DiscreteFieldRequirements (ie. no min ghosts, no max ghosts, # can_split set to True in all directions, all TranspositionStates). input_field_requirements = {} for field, topo_descriptor in self.input_fields.items(): if topo_descriptor is None: req = None else: workdim = topo_descriptor.domain.dim req = DiscreteFieldRequirements(self, self.input_fields, field) req.axes = (self.tstate.axes,) req.memory_order = self.memorder input_field_requirements[field] = req output_field_requirements = {} for field, topo_descriptor in self.output_fields.items(): if topo_descriptor is None: req = None else: workdim = topo_descriptor.domain.dim req = DiscreteFieldRequirements(self, self.output_fields, field) req.axes = (self.tstate.axes,) req.memory_order = self.memorder output_field_requirements[field] = req from hysop.fields.field_requirements import OperatorFieldRequirements requirements = OperatorFieldRequirements() requirements.update_inputs(input_field_requirements) requirements.update_outputs(output_field_requirements) return requirements